package band.wukong.practice.java.extension.topic.script;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.script.*;

/**
 * @author wukong(wukonggg@139.com)
 */
public class JavascriptSample {

    private static final Logger logger = LoggerFactory.getLogger(JavascriptSample.class);

    private ScriptEngine engine;

    public JavascriptSample() {
        ScriptEngineManager manager = new ScriptEngineManager();
        engine = manager.getEngineByName("javascript");
    }

    /**
     * 使用eval方法来执行javascript脚本。
     * <p>
     * 这里运行了一个非常简单的脚本。这个脚本是孤立的，并未通过Java向这脚本传递任何的值。
     * 虽然从这个脚本返回了一个值，但这种返回方式是隐式的。
     */
    public String eval() throws ScriptException {
        String script = "var date = new Date();" +
                "date.getHours();";

        Double hour = (Double) engine.eval(script);
        String meridiem = hour < 12 ? "AM" : "PM";

        return meridiem + " " + hour;
    }

    /**
     * 传递变量，从java到js。
     * <p>
     * 将一个字符串翻转(这个字符串是通过java传给javascript的)，然后通过java得到这个被翻转后的字符后，然后输出。
     */
    public String transferVariable() throws ScriptException {
        engine.put("letters", "ABCDEFG");
        engine.eval("var result ='' ;" +
                "for (i = 0; i <= letters.length; i++) {" +
                "    result = letters.charAt(i) + result" +
                "}");
        return (String) engine.get("result");
    }

    /**
     * 编译
     */
    public String[] compile() throws ScriptException {
        if (!(engine instanceof Compilable)) {
            throw new RuntimeException("engine is not instanceof Compilable.");
        }

        String[] ret = new String[3];

        engine.put("counter", 0);
        Compilable compEngine = (Compilable) engine;
        CompiledScript script = compEngine.compile(
                "function count() { " +
                        "    counter = counter +1; " +
                        "     return counter; " +
                        "}; " +
                        "count();");

        ret[0] = script.eval().toString();
        ret[1] = script.eval().toString();
        ret[2] = script.eval().toString();

        return ret;
    }

    /**
     * 上面的例子只有一个函数，可以通过eval进行调用并将它的值返回。
     * 但如果脚本中有多个函数或想通过用户的输入来决定调用哪个函数，这就需要使用invoke方法进行动态调用
     */
    public String invoke() throws ScriptException, NoSuchMethodException {
        if (!(engine instanceof Invocable)) {
            throw new RuntimeException("engine is not instanceof Invocable.");
        }
        String letters = "ABCDEFG";
        engine.eval(
                "function reverse(letters) {" +
                "    var result =' ';" +
                "    for (i = 0; i <= letters.length; i++) {" +
                "        result = letters.charAt(i) + result" +
                "    } " +
                "    return result;" +
                "}");
        Invocable invokeEngine = (Invocable)engine;
        Object ret = invokeEngine.invokeFunction("reverse", letters);

        return (String) ret;
    }
}
